#include <stdio.h>
#include <io.h>
#include <string.h>
#include <alt_types.h>
#include <system.h>
#include <altera_avalon_pio_regs.h>
#include "eth/eth_rx.h"
#include "eth/eth_tx.h"
#include "eth/ethernet.h"
#include "eth/ipv4.h"
#include "eth/arp.h"
#include "eth/udp.h"
#include "eth/ptsi.h"
#include "eth/fifo_to_amm.h"
//#include "eth/mdio_reg_wr.h"
#include "utility.h"



#define TX_BUF_ETH_OFST		(0x100000)						// Смещение на буфер передатчика сообщений ethernet

#define TX_BUF_ROW_OFST		(0x200000)					// Смещение на буфер передачи строки
#define TST_BUF_OFST		(0x300000)

#define CURRENT_MAC		0x00AABBCC0001LL	//0xd43d7e380096LL//0007edffb1c8
#define BROADCAST_MAC	0xFFFFFFFFFFFFLL
#define CURRENT_IP		conv_ipv4(192, 168, 21, 253)
#define BROADCAST_IP	conv_ipv4(192, 168, 21, 255)

#define ROW_DATA_SIZE	640
#define ROW_FRAME_SIZE	IPV4_HEAD_SIZE + UDP_HEAD_SIZE + PTSI_HEAD_SIZE + PTSI_VIDEO_HEAD_SIZE + 640

typedef struct
{
	alt_u16	row_num;
	alt_u16	frame_num;
	alt_u16	res;
} index_t;

alt_u8 		frame_num;
alt_u64 	mac_ptsi_req;
alt_u32 	ip_ptsi_req;

// ********************************************




alt_u8 rx_eth_frame()
{
	alt_u8		err 			= 0;
	alt_u16 	size			= 0;
	alt_u32 	rx_frame_addr	= 0;
	alt_u32		payload_ofst	= 0;
	alt_u8*		payload;
	alt_u16 	etype			= 0;
	alt_u16 	port			= 0;

	if (eth_rx_get_size() > 0)
	{
		// Считаем адрес пакета. К адресу добавим смещение в 8 байт,
		// чтобы попасть после преамбулы на заголовок
		rx_frame_addr = eth_rx_get_addr_next() + SDRAM_BASE + 8;

		// Проанализируем пакет.
		memset((void*)(TX_BUF_ETH_OFST + SDRAM_BASE), 0, 256);
		err = ethernet_frame_parser((alt_u8*)(rx_frame_addr), &size);
		if (err == 0)
		{
			etype = ethernet_get_etype();
			if (etype == ARP)
			{
				eth_tx_set_etype(ARP);
				eth_tx_set_size(46);
				eth_tx_set_dmac(ethernet_get_smac());
				ipv4_set_destination_ip(ipv4_get_source_ip());
				eth_tx_send(TX_BUF_ETH_OFST, 46);
			}
			else if (etype == IP)
			{
				ipv4_get_payload(&payload_ofst, &size);
				if (ipv4_get_protocol() == UDP)
				{
					payload = (alt_u8*)(rx_frame_addr + ETH_HEAD_SIZE + payload_ofst);
					err = ptsi_msg_parser(payload);
					if (err == 0)
					{
						ipv4_set_destination_ip(ipv4_get_source_ip());
						port	= udp_get_rx_sport();
						udp_set_dport(port);
						// Побайтно передаем все сообщение на выход системы, через PIO
						alt_u16	i = size;
						for (i = 0; i < size; i++)
						{
							IOWR_ALTERA_AVALON_PIO_DATA(PIO_0_BASE, ((1 << 8) | (*payload++)));
							IOWR_ALTERA_AVALON_PIO_DATA(PIO_0_BASE, 0);
						};
					}
				}
				else if (ipv4_get_protocol() == ICMP)
				{
					ipv4_set_destination_ip(ipv4_get_source_ip());

					eth_tx_set_etype(IP);
					eth_tx_set_dmac(ethernet_get_smac());

					ipv4_set_protocol(ICMP);
					ipv4_create_head((void*)(rx_frame_addr + ETH_HEAD_SIZE), size);
					eth_tx_send(rx_frame_addr - SDRAM_BASE + ETH_HEAD_SIZE, size);
				}
			}
		}
		else
		{
			err = 2;
		}
	}
	return err;
}


// ***************************************************
alt_u8 tx_row()
{
	alt_u8		err 		= 0;
	index_t 	index;
	alt_u32 	row_addr;

	// Если есть принятые строки и передатчик свободен,
	err = fifo_to_amm_get_state();
	if (err == 0)
	{
		fifo_to_amm_get_index((void*)&index);
		// Если запрос только получен, надо начать с 1 строки
		if (ptsi_get_row_cnt() == 240)
		{
			while (index.row_num != 1)
			{
				fifo_to_amm_get_index((void*)&index);
				fifo_to_amm_next();
			}
		}
		err = ptsi_row_ctrl();
	}
	else
	{
		err = 17;
	}


	if (err == 0)
	{

		// Для проверки времени выполнения операции установим бит в начале передачи
//		IOWR_ALTERA_AVALON_PIO_DATA(PIO_0_BASE, (1 << 15));

		// Получим адрес блока с данными строки
		fifo_to_amm_get_addr(&row_addr);
		row_addr = row_addr + SDRAM_BASE;


		// Устанавливаем передатчику протокол IP
		eth_tx_set_etype(IP);

		// Добавим IP заголовок
		ipv4_set_protocol(UDP);
		ipv4_create_head(
				(void*)row_addr, ROW_FRAME_SIZE - IPV4_HEAD_SIZE);
		// Добавим UDP заголовок
		udp_create_head(
				(void*)(row_addr + IPV4_HEAD_SIZE),
				ROW_FRAME_SIZE - IPV4_HEAD_SIZE - UDP_HEAD_SIZE);
		// Добавим заголовок видеоданных
		ptsi_create_head_video(
				(void*)(row_addr + IPV4_HEAD_SIZE + UDP_HEAD_SIZE),
				index.row_num,
				index.frame_num,
				row_addr);

		// *** ТЕСТ

//		int			i;
//		alt_u16		rd_data;
//		alt_u16		rd_data_prev;

//		rd_data_prev = IORD(row_addr + IPV4_HEAD_SIZE + UDP_HEAD_SIZE, 21);
//		rd_data = (rd_data_prev >> 8);
		//if (((row_num + 1) != rd_data) && (row_num < 240))
//		if (row_num == 1)
//		{
//			err = 111;
//			for (i=0; i < 150; i++)
//			{
//				rd_data_prev = IORD(row_addr + IPV4_HEAD_SIZE + UDP_HEAD_SIZE + 20, i);
//				rd_data = IORD(row_addr + IPV4_HEAD_SIZE + UDP_HEAD_SIZE + 20, i+1);
//				if (rd_data_prev != rd_data)
//				{
//					err = 111;
//				}
//			}
//		}

		// *** Конец ТЕСТ
		eth_tx_send(row_addr - SDRAM_BASE, ROW_FRAME_SIZE);
		fifo_to_amm_next();
//		usleep(10);
//		i = 0;
//		while (eth_tx_get_state() != 0)
//		{
//			usleep(10);
//			i++;
//		}
//
		// Сбросим бит после установки запроса на передачу
//		IOWR_ALTERA_AVALON_PIO_DATA(PIO_0_BASE, 0);
	}

	return err;
}

// *********************************************

// ************************

int main(void)
{
	alt_u8		err = 0;

	// Настроим приемник ethernet
	eth_rx_init(ETH_RX_0_BASE);
	eth_rx_reset();

	// Настроим передатчик ethernet
	eth_tx_init(ETH_TX_0_BASE);
	eth_tx_reset();
	eth_tx_set_smac(CURRENT_MAC);
	eth_tx_set_dmac(BROADCAST_MAC);

	// Настроим обработчик уровня заголовка ethernet
	ethernet_set_mac(CURRENT_MAC);
	ethernet_set_ip(CURRENT_IP);
	ethernet_set_ask_buf((void*)(TX_BUF_ETH_OFST + SDRAM_BASE));

	// Настроим обработчик уровня заголовка IP
	ipv4_set_source_ip(CURRENT_IP);
	ipv4_set_destination_ip(BROADCAST_IP);

	// Настроим обработчик уровня заголовка UDP
	udp_set_oport(703);
	udp_set_sport(703);
	udp_set_dport(703);

	// Настройка модуля приема строк изображения
	fifo_to_amm_init(FIFO_TO_AMM_0_BASE);
	fifo_to_amm_reset();
	fifo_to_amm_set_fblock_ofst(TX_BUF_ROW_OFST);
	fifo_to_amm_set_frame_ofst(	IPV4_HEAD_SIZE +
								UDP_HEAD_SIZE +
								PTSI_HEAD_SIZE +
								PTSI_VIDEO_HEAD_SIZE);

	// Настройка PtSi
	ptsi_init();

	// ТЕСТОВЫЙ БЛОК
	memset((void*)(TST_BUF_OFST + SDRAM_BASE), 0, 512);
//	while (1)
//	{
//		eth_tx_set_etype(ARP);
//		eth_tx_set_smac(CURRENT_MAC);
//		eth_tx_set_dmac(BROADCAST_MAC);
//		eth_tx_set_size(46);
//		arp_create_header((void*)(TX_BUF_ETH_OFST + SDRAM_BASE));
//
//		eth_tx_send(TX_BUF_ETH_OFST, 46);
//	}
	// КОНЕЦ ТЕСТОВОГО БЛОКА

	// Сбросим приемник
	eth_rx_reset();
	while (1)
	{
//		usleep(10000);
		IOWR_ALTERA_AVALON_PIO_DATA(PIO_0_BASE, 1 << 14);
		// Обработка входных пакетов
		err = rx_eth_frame();
		IOWR_ALTERA_AVALON_PIO_DATA(PIO_0_BASE, 0);

		err =  eth_tx_get_state();
		if (err == 0)
			err = tx_row();
	}


	return 0;
};






